package com.r2coding.system.service.impl;

import com.r2coding.common.exception.BizCode;
import com.r2coding.common.exception.ServiceException;
import com.r2coding.common.util.CommonUtil;
import com.r2coding.common.util.PageUtils;
import com.r2coding.common.util.Query;
import com.r2coding.system.dao.RoleDao;
import com.r2coding.system.entity.PermissionEntity;
import com.r2coding.system.feign.ThirdPartyFeignService;
import com.r2coding.system.service.PermissionService;
import com.r2coding.system.service.RoleService;
import com.r2coding.system.service.WebsiteService;
import com.r2coding.system.vo.req.*;
import com.r2coding.system.vo.resp.WebsiteRespVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.r2coding.system.dao.UserDao;
import com.r2coding.system.entity.UserEntity;
import com.r2coding.system.service.UserService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

/**
 * 
 *
 * @author 98k灬
 * @email hk666zx@163.com
 * @date 2022-04-04 10:30:49
 */
@Slf4j
@Service("userService")
public class UserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements UserService {
    @Autowired
    RoleDao roleDao;

    @Autowired
    PermissionService permissionService;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    PasswordEncoder passwordEncoder;

    @Autowired
    RoleService roleService;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    WebsiteService websiteService;

    @Autowired
    ThirdPartyFeignService thirdPartyFeignService;

    ReentrantLock lock = new ReentrantLock();

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<UserEntity> page = this.page(
                new Query<UserEntity>().getPage(params),
                new QueryWrapper<UserEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public PageUtils queryAll(QueryUserVo vo) {
        QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
        if (StringUtils.isNotEmpty(vo.getUserName())) {
            wrapper.like("user_name", vo.getUserName());
        }
        if (StringUtils.isNotEmpty(vo.getPhone())) {
            wrapper.like("phone", vo.getPhone());
        }
        IPage<UserEntity> page = baseMapper.selectPage(vo.getPage(), wrapper);
        PageUtils pageUtils = new PageUtils(page);
        List<?> list = pageUtils.getList();
        if (!CollectionUtils.isEmpty(list)) {
            List<UserEntity> collect = list.stream().map(item -> {
                UserEntity user = (UserEntity) item;
                String phoneMask = CommonUtil.phoneMask(user.getPhone());
                user.setPhone(phoneMask);
                return user;
            }).collect(Collectors.toList());
            pageUtils.setList(collect);
        }
        return pageUtils;
    }

    @Override
    public List<Integer> getUserRoleIdsByUserId(Integer userId) {
        return this.baseMapper.getUserRoleIdsByUserId(userId);
    }

    @Override
    public void lock(UserLockVo vo) {
        this.baseMapper.lock(vo);
    }

    @Override
    public UserEntity getUserByUserName(String userName) {
        QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("user_name", userName);
        return this.baseMapper.selectOne(wrapper);
    }

    @Override
    public List<PermissionEntity> getPermissionsByUserId(Integer userId) {
        return permissionService.getPermissionsByUserId(userId);
    }

    @Transactional
    @Override
    public void addUser(AddUserReqVo user) {
        if (!CommonUtil.checkPhone(user.getPhone())) {
            throw new ServiceException(BizCode.PHONE_ERROR);
        }

        if (!CommonUtil.checkUserName(user.getUserName())) {
            throw new ServiceException(BizCode.USERNAME_ERROR);
        }
        // 判断用户名或者手机号是否重复
        this.checkUserNameOrPhoneRepeat(null, user.getUserName(), user.getPhone());

        if (user.getCode() == null) {
            throw new ServiceException(BizCode.PHONE_CODE_ERROR);
        }
        if (!stringRedisTemplate.hasKey(user.getPhone())) {
            throw new ServiceException(BizCode.PHONE_CODE_ERROR);
        } else {
            String codeFormRedis = stringRedisTemplate.opsForValue().get(user.getPhone());
            log.info("codeFormRedis: {}", codeFormRedis);
            log.info("codeFormRequest: {}", user.getCode());
            if (!codeFormRedis.equals(user.getCode())) {
                throw new ServiceException(BizCode.PHONE_CODE_ERROR);
            }
        }
        UserEntity userEntity = new UserEntity();
        BeanUtils.copyProperties(user, userEntity);
        userEntity.setPassword(passwordEncoder.encode(user.getPassword()));
        userEntity.setDeleted(0);
        userEntity.setUpdateTime(new Date());
        userEntity.setCreateTime(new Date());
        userEntity.setStatus(1);
        userEntity.setSex(1);
        // 给注册用户分配默认的头像
        WebsiteRespVo info = websiteService.info();
        String avatar = info.getVisitAvatar();
        userEntity.setAvatar(avatar);
        this.baseMapper.insert(userEntity);
        // 给注册用户分配默认的访客角色
        AddUserRoleReqVo vo = new AddUserRoleReqVo();
        vo.setUserId(userEntity.getUserId());
        vo.setRoleIds(Arrays.asList(3));
        roleService.addUserRole(vo);
        log.info("User Register success.");
    }

    @Override
    public void updateEmail(UpdateEmailReqVo req) {
        // 判断code是否有效
        if (!stringRedisTemplate.hasKey(req.getEmail())) {
            throw new ServiceException(BizCode.EMAIL_CODE_ERROR);
        }
        QueryWrapper<UserEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_name", req.getUserName());
        UserEntity user = this.baseMapper.selectOne(queryWrapper);
        if (user == null) {
            throw new ServiceException(BizCode.SERVICE_EXCEPTION);
        }
        user.setUpdateTime(new Date());
        user.setEmail(req.getEmail());
        this.baseMapper.updateById(user);
    }

    @Override
    public void updateUser(UserEntity user) {
        if (!CommonUtil.checkPhone(user.getPhone())) {
            throw new ServiceException(BizCode.PHONE_ERROR);
        }

        if (!CommonUtil.checkUserName(user.getUserName())) {
            throw new ServiceException(BizCode.USERNAME_ERROR);
        }

        // 判断用户名或者手机号是否重复
        this.checkUserNameOrPhoneRepeat(user.getUserId(),user.getUserName(), user.getPhone());

        user.setUpdateTime(new Date());
        this.baseMapper.updateById(user);
    }

    @Override
    public void updatePhone(UpdatePhoneReqVo req) {
        // 判断用户名或者手机号是否重复
        this.checkUserNameOrPhoneRepeat(req.getUserId(), null, req.getPhone());
        if (!CommonUtil.checkPhone(req.getPhone())) {
            throw new ServiceException(BizCode.PHONE_ERROR);
        }

        // 判断code是否有效
        if (!stringRedisTemplate.hasKey(req.getPhone())) {
            throw new ServiceException(BizCode.PHONE_CODE_ERROR);
        }
        UserEntity user = new UserEntity();
        BeanUtils.copyProperties(req, user);
        user.setUpdateTime(new Date());
        this.baseMapper.updateById(user);
    }

    private void checkUserNameOrPhoneRepeat(Integer userId, String name, String phone) {
        try {
            lock.lock();
            if (!StringUtils.isEmpty(name)) {
                QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
                wrapper.eq("user_name", name);
                Integer count = this.baseMapper.selectCount(wrapper);
                if (count > 0 && userId == null) {
                    throw new ServiceException(BizCode.USERNAME_REPEAT);
                }
                if (count > 1) {
                    throw new ServiceException(BizCode.USERNAME_REPEAT);
                }
            }

            if (!StringUtils.isEmpty(phone)) {
                QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
                wrapper.eq("phone", phone);
                Integer count = this.baseMapper.selectCount(wrapper);
                if (count > 0 && userId == null) {
                    throw new ServiceException(BizCode.PHONE_REPEAT);
                }
                if (count > 1) {
                    throw new ServiceException(BizCode.PHONE_REPEAT);
                }
            }
        } finally {
            lock.unlock();
        }

    }

    @Override
    public void updatePassword(UpdatePasswordReqVo req) {
        QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("phone", req.getPhone());
        UserEntity user = this.baseMapper.selectOne(wrapper);
        if (user == null) {
            throw new ServiceException(BizCode.USER_NOT_EXIST);
        }

        if (!stringRedisTemplate.hasKey(req.getPhone())) {
            throw new ServiceException(BizCode.PHONE_CODE_ERROR);
        }

        String codeFromRedis = stringRedisTemplate.opsForValue().get(req.getPhone());
        if (!codeFromRedis.equals(req.getCode())) {
            throw new ServiceException(BizCode.PHONE_CODE_ERROR);
        }

        // 密码加密
        String encode = passwordEncoder.encode(req.getPassword());

        user.setPassword(encode);
        user.setUpdateTime(new Date());
        this.baseMapper.updateById(user);
    }

    // @CacheEvict(value = "user", key = " 'USER_INFO_' + #userId ")
    @Transactional
    @Override
    public void deleteUser(Integer userId) {
        // 删除该用户关联的角色
        this.baseMapper.deleteUserRole(userId);
        // 删除该用户
        this.baseMapper.deleteById(userId);
    }

    // @Cacheable(value = "user", key = " 'USER_INFO_' + #userId ")
    @Override
    public UserEntity getUserById(Integer userId) {
        UserEntity user = this.baseMapper.selectById(userId);
        // 将身份证号码加*
        if (user != null) {
            String phoneMask = CommonUtil.phoneMask(user.getPhone());
            user.setPhone(phoneMask);
        }
        return user;
    }
}